﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RuanGong.background.A;
using RuanGong.background.A.Classes;

namespace RuanGong.background.B
{
    internal class ProgramAPI
    {
        
        public static List<Local_Healthy_DataBase> Find(string name)
        {
            var LHD = LocalHealthyDataBaseAPI.readAll();
            generateMA();
            var data=new List<Local_Healthy_DataBase>();
            for(int i = 0; i < LHD.Count; i++)
            {
                if (LHD[i].name==name) data.Add(LHD[i]);
            }
            Console.WriteLine(data.Count);
            return data;
        }

        public static void generateMA()
        {
            var LHD = LocalHealthyDataBaseAPI.readAll();
            foreach(var item in LHD)
            {
                if (item.medicaladvice == null)
                {
                    K_Means_Algorithm kma = new K_Means_Algorithm(LHD, 3);
                    kma.Execute();
                    item.medicaladvice = LHD[kma.Add(new Point(item.bloodfat, item.bloodsugar, item.diastolicpressure, item.systolicpressure, 0))].medicaladvice;
                }
            }
        }

        public static string ReadMA(string name)
        {
            var found = Find(name);
            string s = "";
            foreach (var item in found)
            {
                s += item.medicaladvice+'\n';
            }
            return s;
        }

        public List<double> ReadBf(string name)
        {
            var found = Find(name);
            var data= new List<double>();
            foreach (var item in found)
            {
                data.Add(item.bloodfat);
            }
            return data;
        }

        public static List<double> ReadBs(string name)
        {
            var found = Find(name);
            var data = new List<double>();
            foreach (var item in found)
            {
                data.Add(item.bloodsugar);
            }
            return data;
        }

        public static List<double> ReadD(string name)
        {
            var found = Find(name);
            var data = new List<double>();
            foreach (var item in found)
            {
                data.Add(item.diastolicpressure);
            }
            return data;
        }

        public static List<double> ReadS(string name)
        {
            var found = Find(name);
            var data = new List<double>();
            foreach (var item in found)
            {
                data.Add(item.systolicpressure);
            }
            return data;
        }
    }

    internal class Point
    {
        public double X { get; set; }
        public double Y { get; set; }
        public double Z { get; set; }
        public double W { get; set; }
        public int N { get; set; }
        public Point(double x, double y, double z,double w, int n)
        {
            X = x;
            Y = y;
            Z = z;
            W = w;
            N = n;
        }
    }

    /// <summary>
    /// K-Means算法
    /// </summary>
    internal class K_Means_Algorithm
    {
        private static int K { get; set; } = 0;
        private List<Point> points { get; set; } = new List<Point>();
        private Point[] z = new Point[K];

        public K_Means_Algorithm(List<Local_Healthy_DataBase> p, int k)
        {
            K = k;
            for(int i =0;i<p.Count;i++)
            {
                var lhd = p[i];
                points.Add(new Point(lhd.bloodfat,lhd.bloodsugar,lhd.diastolicpressure,lhd.systolicpressure,i));
            }
        }

        public int Add(Point point)
        {
            double d = Distance(point, z[0]);
            int close=0;
            for (int i = 1; i < K; i++)
            {
                if (Distance(point, z[i]) < d) close = i;
            }
            Random random = new Random();
            List<Point> p = Execute()[close];
            int r=random.Next(0, p.Capacity);
            return p[r].N;
        }

        /// <summary>
        /// 计算新的聚类中心 
        /// </summary>
        /// <param name="m"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public Point Center_Point(int m, int[] type)
        {
            int count = 0;
            Point sum = new Point(0.0, 0.0,0.0,0.0,0);
            for (int i = 0; i < points.Count; i++)
            {
                if (type[i] == m)
                {
                    sum.X = points[i].X + sum.X;
                    sum.Y = points[i].Y + sum.Y;
                    sum.Z = points[i].Z + sum.Z;
                    sum.W = points[i].W + sum.W;
                    count++;
                }
            }
            if (count > 0)
            {
                sum.X = sum.X / count;
                sum.Y = sum.Y / count;
                sum.Z = sum.Z / count;
                sum.W = sum.W / count;
            }
            return sum;
        }

        /// <summary>
        /// 比较两个聚类中心的是否相等
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        private bool Compare(Point a, Point b)
        {
            if ((a.X) == (b.X) && (a.Y) == (b.Y)&&(a.Z) == (b.Z)&&(a.W) == (b.W))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 进行迭代，对total个样本根据聚类中心进行分类
        /// </summary>
        /// <param name="type"></param>
        /// <param name="z"></param>
        private void Order(ref int[] type, Point[] z)
        {
            //记录unknown[i]暂时在哪个类中 
            int temp = 0;
            for (int i = 0; i < points.Count; i++)
            {
                for (int j = 0; j < K; j++)
                {
                    if (Distance(points[i], z[temp]) > Distance(points[i], z[j]))
                    {
                        temp = j;
                    }
                }
                type[i] = temp;
            }
        }

        /// <summary>
        /// 计算两个点的欧式距离
        /// </summary>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        /// <returns></returns>
        private double Distance(Point p1, Point p2)
        {
            return ((p1.X - p2.X) * (p1.X - p2.X) + (p1.Y - p2.Y) * (p1.Y - p2.Y)+ (p1.Z - p2.Z) * (p1.Z - p2.Z)+(p1.W - p2.W) * (p1.W - p2.W));
        }

        /// <summary>
        /// 进行聚类
        /// </summary>
        /// <returns></returns>
        public List<List<Point>> Execute()
        {
            int[] type = new int[points.Count];

            
            Point[] z0 = new Point[K];
            for (int i = 0; i < K; i++)
            {
                z[i] = points[i];
            }

            List<List<Point>> result = new List<List<Point>>();

            int test = 0;
            int loop = 0;
            while (test != K)
            {
                Order(ref type, z);
                for (int i = 0; i < K; i++)
                {
                    z[i] = Center_Point(i, type);
                    if (Compare(z[i], z0[i]))
                    {
                        test = test + 1;
                    }
                    else
                    {
                        z0[i] = z[i];
                    }
                }

                loop = loop + 1;

                List<Point> p = new List<Point>();
                for (int j = 0; j < K; j++)
                {
                    for (int i = 0; i < points.Count; i++)
                    {
                        if (type[i] == j)
                        {
                            p.Add(points[i]);
                        }
                    }
                }
                if (p.Count > 0)
                {
                    result.Add(p);
                }
            }

            return result;
        }
    }

}
